package org.myspringframework.core.inject;

import lombok.extern.slf4j.Slf4j;
import org.myspringframework.core.BeanContainer;
import org.myspringframework.core.inject.annotation.Autowired;
import org.myspringframework.core.util.ClassUtil;
import org.myspringframework.core.util.CollectionUtils;
import org.myspringframework.core.util.StringUtils;

import java.lang.reflect.Field;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

/**
 * @author jaymin<br>
 * 提供依赖注入的能力<br>
 * 2020/11/19 23:24
 */
@Slf4j
@SuppressWarnings("all")
public class DependencyInjector {
    /**
     * Bean容器
     */
    private BeanContainer beanContainer;

    public DependencyInjector() {
        beanContainer = BeanContainer.getInstance();
    }

    /**
     * 执行IOC
     */
    public void doIoc() {
        if (Objects.isNull(beanContainer)) {
            log.warn("empty classset in BeanContainer");
            return;
        }
        //1. 遍历Bean容器所有的Class对象
        for (Class<?> clazz : beanContainer.getClasses()) {
            //2. 遍历Class对象的所有成员变量
            Field[] fields = clazz.getDeclaredFields();
            if (CollectionUtils.isEmpty(fields)) {
                continue;
            }
            for (Field field : fields) {
                //3. 找出被Autowired标记的成员变量
                if (field.isAnnotationPresent(Autowired.class)) {
                    Autowired autowired = field.getAnnotation(Autowired.class);
                    String autowiredValue = autowired.value();
                    //4. 获取这些成员变量的类型
                    Class<?> fieldClass = field.getType();
                    //5. 获取这些成员变量的类型在容器中的实例
                    Object fieldValue = getFieldInstance(fieldClass, autowiredValue);
                    if (Objects.isNull(fieldValue)) {
                        throw new RuntimeException("unable to inject relevant type which target class is " + fieldClass.getName() + "Autowired :" + autowiredValue);
                    }
                    //6. 通过反射将对应的成员变量实例注入到成员变量所在类的实例中
                    Object targetBean = beanContainer.getBean(clazz);
                    ClassUtil.setField(field, targetBean, fieldValue, true);
                }
            }


        }


    }

    /**
     * 根据Class对象在beanContainer中获取其实例或者实现类
     *
     * @param fieldClass
     * @param autowiredValue
     * @return
     */
    private Object getFieldInstance(Class<?> fieldClass, String autowiredValue) {
        Object fieldValue = beanContainer.getBean(fieldClass);
        // 如果是接口,fieldValue会为空，因为容器中只存储其实现类
        if (Objects.nonNull(fieldValue)) {
            return fieldValue;
        }
        Set<Class<?>> classSet = Optional.ofNullable(beanContainer.getClassesBySuper(fieldClass))
                .orElse(null);
        if (CollectionUtils.isEmpty(classSet)) {
            return null;
        }
        if (StringUtils.isEmpty(autowiredValue)) {
            boolean isSingleImpl = classSet.size() == 1;
            if (isSingleImpl) {
                return beanContainer.getBean(classSet.iterator().next());
            }
            throw new RuntimeException("multiple implemented classes for" + fieldClass.getName() + "please set @Autowired's value to pick one");
        }
        Class<?> targetClass = classSet.stream().filter(clazz -> Objects.equals(clazz.getSimpleName(), autowiredValue)).findFirst().get();
        return beanContainer.getBean(targetClass);
    }

}



